﻿namespace _4_NovaAdmin.Web.Core.Middlewares;

public class IdempotencyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IdempotencyStoreFactory _storeFactory;
    private readonly ILogger<IdempotencyMiddleware> _logger;

    // 内存锁字典（跟踪正在处理的请求）
    private static readonly ConcurrentDictionary<string, object> _processingLocks = new();

    public IdempotencyMiddleware(RequestDelegate next, IdempotencyStoreFactory storeFactory, ILogger<IdempotencyMiddleware> logger)
    {
        _next = next;
        _storeFactory = storeFactory;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 跳过GET请求
        if (context.Request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
        {
            await _next(context);
            return;
        }

        // 检查Idempotency-Key
        if (!context.Request.Headers.TryGetValue("Idempotency-Key", out var idempotencyKey) ||
            string.IsNullOrEmpty(idempotencyKey))
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
            await context.Response.WriteAsync("Idempotency-Key header is required");
            return;
        }

        var storeKey = $"{context.Request.Method}:{context.Request.Path}:{idempotencyKey}";
        var store = _storeFactory.CreateStore();
        var originalStream = context.Response.Body;

        // 获取当前请求的专用锁对象
        var requestLock = _processingLocks.GetOrAdd(storeKey, _ => new object());

        lock (requestLock) // 单体内存锁
        {
            try
            {
                // 双重检查缓存
                var cachedResponse = store.GetResponseAsync(storeKey).GetAwaiter().GetResult();
                if (!string.IsNullOrEmpty(cachedResponse))
                {
                    context.Response.StatusCode = StatusCodes.Status200OK;
                    context.Response.ContentType = "application/json";
                    context.Response.WriteAsync(cachedResponse).GetAwaiter().GetResult();
                    return;
                }

                // 执行请求并捕获响应
                using var memoryStream = new MemoryStream();
                context.Response.Body = memoryStream;
                _next(context).GetAwaiter().GetResult();

                memoryStream.Seek(0, SeekOrigin.Begin);
                var responseBody = new StreamReader(memoryStream).ReadToEnd();

                // 存储成功响应 500 就是报错了，没执行成功 就不用添加了
                if (context.Response.StatusCode >= 200 && context.Response.StatusCode < 300)
                {
                    store.TryCreateRecordAsync(storeKey, responseBody).GetAwaiter().GetResult();
                }

                // 回写响应
                memoryStream.Seek(0, SeekOrigin.Begin);
                memoryStream.CopyToAsync(originalStream).GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Idempotency processing failed");
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                context.Response.WriteAsync("Internal Server Error").GetAwaiter().GetResult();
            }
            finally
            {
                context.Response.Body = originalStream;
                _processingLocks.TryRemove(storeKey, out _); // 清理锁
            }
        }
    }

    //public async Task InvokeAsync(HttpContext context)
    //{
    //    // 保存原始响应流
    //    var originalBodyStream = context.Response.Body;
    //    if (context.Request.Method.ToUpper().Equals("GET"))
    //    {
    //        await _next(context);
    //        return;
    //    }
    //    context.Request.Headers.TryGetValue("Idempotency-Key", out var idempotencyKey);
    //    if (string.IsNullOrEmpty(idempotencyKey))
    //    {
    //        context.Response.StatusCode = StatusCodes.Status400BadRequest;
    //        await context.Response.WriteAsync("Idempotency-Key header is required");
    //        return;
    //    }

    //    var storeKey = $"{context.Request.Method}:{context.Request.Path}:{idempotencyKey}";
    //    var store = _storeFactory.CreateStore();

    //    try
    //    {
    //        var cachedResponse = await store.GetResponseAsync(storeKey);
    //        if (!string.IsNullOrEmpty(cachedResponse))
    //        {
    //            context.Response.StatusCode = StatusCodes.Status200OK;
    //            context.Response.ContentType = "application/json";
    //            await context.Response.WriteAsync(cachedResponse);
    //            return;
    //        }

    //        // 创建内存流替换原始流
    //        using var memoryStream = new MemoryStream();
    //        context.Response.Body = memoryStream;
    //        await _next(context);
    //        // 读取响应内容
    //        memoryStream.Seek(0, SeekOrigin.Begin);
    //        var responseBody = await new StreamReader(memoryStream).ReadToEndAsync();
    //        // 将内存流内容复制回原始流
    //        memoryStream.Seek(0, SeekOrigin.Begin);
    //        await memoryStream.CopyToAsync(originalBodyStream);
    //        await store.TryCreateRecordAsync(storeKey, responseBody);
    //    }
    //    catch (Exception)
    //    {
    //    }
    //    finally
    //    {
    //        // 恢复原始流
    //        context.Response.Body = originalBodyStream;
    //    }
    //}
}
//HTTP 响应流默认不可读，是协议规范、性能、安全与设计简洁性综合权衡的结果，开发者可通过中间件按需扩展功能
//HTTP响应流就像一根 ​​单行道​​，设计上就是只能从服务器往浏览器方向发送数据。 原装响应流 ​​没做读取功能​​，只能自己换一个带读取功能的流！
//简单来说，​​HTTP响应流就像一根只能出水的管子​​：

//​​1.原始管子（context.Response.Body）​​
//  只能往外送水（写数据给浏览器），​​不能把水抽回来读​​。
//​​2.你需要一个水桶（MemoryStream）​​
//  暂时把管子接到水桶上，让水流进水桶。
//  这样你就能看到桶里的水（读取响应内容）。
//  看完后，把水桶的水倒回原来的管子（CopyToAsync），让水流到浏览器。
//​​不换水桶的话，你永远看不到水流过的内容！​​
//这就是为什么必须用内存流替换一下。